/*
Copyright (C) 2018-2019 de4dot@gmail.com

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#if NASM
using System;
using System.Diagnostics;
using Iced.Intel.FormatterInternal;

namespace Iced.Intel.NasmFormatterInternal {
	// GENERATOR-BEGIN: InstrOpKind
	// ⚠️This was generated by GENERATOR!🦹‍♂️
	enum InstrOpKind : byte {
		Register,
		NearBranch16,
		NearBranch32,
		NearBranch64,
		FarBranch16,
		FarBranch32,
		Immediate8,
		Immediate8_2nd,
		Immediate16,
		Immediate32,
		Immediate64,
		Immediate8to16,
		Immediate8to32,
		Immediate8to64,
		Immediate32to64,
		MemorySegSI,
		MemorySegESI,
		MemorySegRSI,
		MemorySegDI,
		MemorySegEDI,
		MemorySegRDI,
		MemoryESDI,
		MemoryESEDI,
		MemoryESRDI,
		Memory64,
		Memory,
		Sae,
		RnSae,
		RdSae,
		RuSae,
		RzSae,
		DeclareByte,
		DeclareWord,
		DeclareDword,
		DeclareQword,
	}
	// GENERATOR-END: InstrOpKind

	// GENERATOR-BEGIN: MemorySizeInfo
	// ⚠️This was generated by GENERATOR!🦹‍♂️
	enum MemorySizeInfo {
		None,
		Word,
		Dword,
		Qword,
	}
	// GENERATOR-END: MemorySizeInfo

	// GENERATOR-BEGIN: FarMemorySizeInfo
	// ⚠️This was generated by GENERATOR!🦹‍♂️
	enum FarMemorySizeInfo {
		None,
		Word,
		Dword,
	}
	// GENERATOR-END: FarMemorySizeInfo

	struct InstrOpInfo {
		internal const int TEST_RegisterBits = IcedConstants.RegisterBits;

		public FormatterString Mnemonic;
		public InstrOpInfoFlags Flags;
		public byte OpCount;
		public InstrOpKind Op0Kind;
		public InstrOpKind Op1Kind;
		public InstrOpKind Op2Kind;
		public InstrOpKind Op3Kind;
		public InstrOpKind Op4Kind;
		public byte Op0Register;
		public byte Op1Register;
		public byte Op2Register;
		public byte Op3Register;
		public byte Op4Register;
		public sbyte Op0Index;
		public sbyte Op1Index;
		public sbyte Op2Index;
		public sbyte Op3Index;
		public sbyte Op4Index;

		public MemorySize MemorySize {
			readonly get => (MemorySize)(((uint)Flags >> (int)InstrOpInfoFlags.MemorySizeShift) & (uint)InstrOpInfoFlags.MemorySizeMask);
			set => Flags = (InstrOpInfoFlags)(((uint)Flags & ~((uint)InstrOpInfoFlags.MemorySizeMask << (int)InstrOpInfoFlags.MemorySizeShift)) |
				(((uint)value & (uint)InstrOpInfoFlags.MemorySizeMask) << (int)InstrOpInfoFlags.MemorySizeShift));
		}

		public readonly int GetOpRegister(int operand) =>
			operand switch {
				0 => Op0Register,
				1 => Op1Register,
				2 => Op2Register,
				3 => Op3Register,
				4 => Op4Register,
				_ => throw new ArgumentOutOfRangeException(nameof(operand)),
			};

		public readonly InstrOpKind GetOpKind(int operand) {
			switch (operand) {
			case 0: return Op0Kind;
			case 1: return Op1Kind;
			case 2: return Op2Kind;
			case 3: return Op3Kind;
			case 4: return Op4Kind;
			default:
				Debug.Assert(Op0Kind == InstrOpKind.DeclareByte || Op0Kind == InstrOpKind.DeclareWord || Op0Kind == InstrOpKind.DeclareDword || Op0Kind == InstrOpKind.DeclareQword);
				return Op0Kind;
			}
		}

		public readonly int GetInstructionIndex(int operand) {
			int instructionOperand;
			switch (operand) {
			case 0: instructionOperand = Op0Index; break;
			case 1: instructionOperand = Op1Index; break;
			case 2: instructionOperand = Op2Index; break;
			case 3: instructionOperand = Op3Index; break;
			case 4: instructionOperand = Op4Index; break;
			default:
				Debug.Assert(Op0Kind == InstrOpKind.DeclareByte || Op0Kind == InstrOpKind.DeclareWord || Op0Kind == InstrOpKind.DeclareDword || Op0Kind == InstrOpKind.DeclareQword);
				instructionOperand = -1;
				break;
			}
			return instructionOperand < 0 ? -1 : instructionOperand;
		}

#if INSTR_INFO
		public readonly bool TryGetOpAccess(int operand, out OpAccess access) {
			int instructionOperand;
			switch (operand) {
			case 0: instructionOperand = Op0Index; break;
			case 1: instructionOperand = Op1Index; break;
			case 2: instructionOperand = Op2Index; break;
			case 3: instructionOperand = Op3Index; break;
			case 4: instructionOperand = Op4Index; break;
			default:
				Debug.Assert(Op0Kind == InstrOpKind.DeclareByte || Op0Kind == InstrOpKind.DeclareWord || Op0Kind == InstrOpKind.DeclareDword || Op0Kind == InstrOpKind.DeclareQword);
				instructionOperand = Op0Index;
				break;
			}
			if (instructionOperand < InstrInfo.OpAccess_INVALID) {
				access = (OpAccess)(-instructionOperand - 2);
				return true;
			}
			access = OpAccess.None;
			return false;
		}
#endif

		public readonly int GetOperandIndex(int instructionOperand) {
			int index;
			if (instructionOperand == Op0Index)
				index = 0;
			else if (instructionOperand == Op1Index)
				index = 1;
			else if (instructionOperand == Op2Index)
				index = 2;
			else if (instructionOperand == Op3Index)
				index = 3;
			else if (instructionOperand == Op4Index)
				index = 4;
			else
				index = -1;
			return index < OpCount ? index : -1;
		}

		public InstrOpInfo(FormatterString mnemonic, in Instruction instruction, InstrOpInfoFlags flags) {
			Static.Assert(IcedConstants.MaxOpCount == 5 ? 0 : -1);
			Mnemonic = mnemonic;
			Flags = flags | (InstrOpInfoFlags)((uint)instruction.MemorySize << (int)InstrOpInfoFlags.MemorySizeShift);
			Op0Kind = (InstrOpKind)instruction.Op0Kind;
			Op1Kind = (InstrOpKind)instruction.Op1Kind;
			Op2Kind = (InstrOpKind)instruction.Op2Kind;
			Op3Kind = (InstrOpKind)instruction.Op3Kind;
			Op4Kind = (InstrOpKind)instruction.Op4Kind;
			Static.Assert(TEST_RegisterBits == 8 ? 0 : -1);
			Op0Register = (byte)instruction.Op0Register;
			Static.Assert(TEST_RegisterBits == 8 ? 0 : -1);
			Op1Register = (byte)instruction.Op1Register;
			Static.Assert(TEST_RegisterBits == 8 ? 0 : -1);
			Op2Register = (byte)instruction.Op2Register;
			Static.Assert(TEST_RegisterBits == 8 ? 0 : -1);
			Op3Register = (byte)instruction.Op3Register;
			Static.Assert(TEST_RegisterBits == 8 ? 0 : -1);
			Op4Register = (byte)instruction.Op4Register;
			int opCount = instruction.OpCount;
			OpCount = (byte)opCount;
			switch (opCount) {
			case 0:
				Op0Index = InstrInfo.OpAccess_INVALID;
				Op1Index = InstrInfo.OpAccess_INVALID;
				Op2Index = InstrInfo.OpAccess_INVALID;
				Op3Index = InstrInfo.OpAccess_INVALID;
				Op4Index = InstrInfo.OpAccess_INVALID;
				break;

			case 1:
				Op0Index = 0;
				Op1Index = InstrInfo.OpAccess_INVALID;
				Op2Index = InstrInfo.OpAccess_INVALID;
				Op3Index = InstrInfo.OpAccess_INVALID;
				Op4Index = InstrInfo.OpAccess_INVALID;
				break;

			case 2:
				Op0Index = 0;
				Op1Index = 1;
				Op2Index = InstrInfo.OpAccess_INVALID;
				Op3Index = InstrInfo.OpAccess_INVALID;
				Op4Index = InstrInfo.OpAccess_INVALID;
				break;

			case 3:
				Op0Index = 0;
				Op1Index = 1;
				Op2Index = 2;
				Op3Index = InstrInfo.OpAccess_INVALID;
				Op4Index = InstrInfo.OpAccess_INVALID;
				break;

			case 4:
				Op0Index = 0;
				Op1Index = 1;
				Op2Index = 2;
				Op3Index = 3;
				Op4Index = InstrInfo.OpAccess_INVALID;
				break;

			case 5:
				Op0Index = 0;
				Op1Index = 1;
				Op2Index = 2;
				Op3Index = 3;
				Op4Index = 4;
				break;

			default:
				throw new InvalidOperationException();
			}
		}
	}

	abstract class InstrInfo {
		public const int OpAccess_INVALID = -1;
#if INSTR_INFO
		public const int OpAccess_None = -(int)(OpAccess.None + 2);
		public const int OpAccess_Read = -(int)(OpAccess.Read + 2);
		public const int OpAccess_CondRead = -(int)(OpAccess.CondRead + 2);
		public const int OpAccess_Write = -(int)(OpAccess.Write + 2);
		public const int OpAccess_CondWrite = -(int)(OpAccess.CondWrite + 2);
		public const int OpAccess_ReadWrite = -(int)(OpAccess.ReadWrite + 2);
		public const int OpAccess_ReadCondWrite = -(int)(OpAccess.ReadCondWrite + 2);
		public const int OpAccess_NoMemAccess = -(int)(OpAccess.NoMemAccess + 2);
#else
		public const int OpAccess_None = OpAccess_INVALID;
		public const int OpAccess_Read = OpAccess_INVALID;
		public const int OpAccess_CondRead = OpAccess_INVALID;
		public const int OpAccess_Write = OpAccess_INVALID;
		public const int OpAccess_CondWrite = OpAccess_INVALID;
		public const int OpAccess_ReadWrite = OpAccess_INVALID;
		public const int OpAccess_ReadCondWrite = OpAccess_INVALID;
		public const int OpAccess_NoMemAccess = OpAccess_INVALID;
#endif

		public abstract void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info);

		protected static int GetBitness(CodeSize codeSize) =>
			codeSize switch {
				CodeSize.Code16 => 16,
				CodeSize.Code32 => 32,
				CodeSize.Code64 => 64,
				_ => 0,
			};
	}

	sealed class SimpleInstrInfo : InstrInfo {
		readonly FormatterString mnemonic;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo(string mnemonic) : this(mnemonic, InstrOpInfoFlags.None) { }

		public SimpleInstrInfo(string mnemonic, InstrOpInfoFlags flags) {
			this.mnemonic = new FormatterString(mnemonic);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) =>
			info = new InstrOpInfo(mnemonic, instruction, flags);
	}

	sealed class SimpleInstrInfo_cc : InstrInfo {
		readonly int ccIndex;
		readonly FormatterString[] mnemonics;

		public SimpleInstrInfo_cc(int ccIndex, string[] mnemonics) {
			this.ccIndex = ccIndex;
			this.mnemonics = FormatterString.Create(mnemonics);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			const InstrOpInfoFlags flags = InstrOpInfoFlags.None;
			var mnemonic = MnemonicCC.GetMnemonicCC(options, ccIndex, mnemonics);
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_mmxmem : InstrInfo {
		readonly FormatterString mnemonic;
		readonly InstrOpInfoFlags flags;
		readonly MemorySize memSize;

		public SimpleInstrInfo_mmxmem(string mnemonic) : this(mnemonic, InstrOpInfoFlags.None, MemorySize.Unknown) { }
		public SimpleInstrInfo_mmxmem(string mnemonic, InstrOpInfoFlags flags) : this(mnemonic, flags, MemorySize.Unknown) { }

		public SimpleInstrInfo_mmxmem(string mnemonic, InstrOpInfoFlags flags, MemorySize memSize) {
			this.mnemonic = new FormatterString(mnemonic);
			this.flags = flags;
			this.memSize = memSize;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, flags);
			if (memSize != MemorySize.Unknown)
				info.MemorySize = memSize;
		}
	}

	sealed class SimpleInstrInfo_SEX1 : InstrInfo {
		readonly int bitness;
		readonly SignExtendInfo sexInfo;
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_SEX1(int bitness, SignExtendInfo sexInfo, string mnemonic) {
			this.bitness = bitness;
			this.sexInfo = sexInfo;
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = (InstrOpInfoFlags)((int)sexInfo << (int)InstrOpInfoFlags.SignExtendInfoShift);

			int instrBitness = GetBitness(instruction.CodeSize);
			if (bitness != 0 && instrBitness != 0 && instrBitness != bitness) {
				if (bitness == 16)
					flags |= InstrOpInfoFlags.OpSize16;
				else if (bitness == 32)
					flags |= InstrOpInfoFlags.OpSize32;
				else
					flags |= InstrOpInfoFlags.OpSize64;
			}

			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_SEX1a : InstrInfo {
		readonly int bitness;
		readonly SignExtendInfo sexInfo;
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_SEX1a(int bitness, SignExtendInfo sexInfo, string mnemonic) {
			this.bitness = bitness;
			this.sexInfo = sexInfo;
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;

			bool signExtend = true;
			int instrBitness = GetBitness(instruction.CodeSize);
			if (bitness != 0 && instrBitness != 0 && instrBitness != bitness) {
				if (instrBitness == 64)
					flags |= InstrOpInfoFlags.OpSize16;
			}
			else if (bitness == 16 && instrBitness == 16)
				signExtend = false;

			if (signExtend)
				flags |= (InstrOpInfoFlags)((int)sexInfo << (int)InstrOpInfoFlags.SignExtendInfoShift);

			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_SEX2 : InstrInfo {
		readonly SignExtendInfo sexInfoReg;
		readonly SignExtendInfo sexInfoMem;
		readonly FormatterString mnemonic;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_SEX2(SignExtendInfo sexInfo, string mnemonic) : this(sexInfo, sexInfo, mnemonic, InstrOpInfoFlags.None) { }
		public SimpleInstrInfo_SEX2(SignExtendInfo sexInfo, string mnemonic, InstrOpInfoFlags flags) : this(sexInfo, sexInfo, mnemonic, flags) { }

		public SimpleInstrInfo_SEX2(SignExtendInfo sexInfoReg, SignExtendInfo sexInfoMem, string mnemonic, InstrOpInfoFlags flags) {
			this.sexInfoReg = sexInfoReg;
			this.sexInfoMem = sexInfoMem;
			this.mnemonic = new FormatterString(mnemonic);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			Debug.Assert(instruction.OpCount == 2);
			var sexInfo = instruction.Op0Kind == OpKind.Memory || instruction.Op1Kind == OpKind.Memory ? sexInfoMem : sexInfoReg;
			var flags = this.flags;
			flags |= (InstrOpInfoFlags)((int)sexInfo << (int)InstrOpInfoFlags.SignExtendInfoShift);
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_SEX3 : InstrInfo {
		readonly SignExtendInfo sexInfo;
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_SEX3(SignExtendInfo sexInfo, string mnemonic) {
			this.sexInfo = sexInfo;
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = (InstrOpInfoFlags)((int)sexInfo << (int)InstrOpInfoFlags.SignExtendInfoShift);
			info = new InstrOpInfo(mnemonic, instruction, flags);
			Debug.Assert(info.OpCount == 3);
			if (options.UsePseudoOps && info.Op0Kind == InstrOpKind.Register && info.Op1Kind == InstrOpKind.Register && info.Op0Register == info.Op1Register) {
				info.OpCount--;
				info.Op0Index = OpAccess_ReadWrite;
				info.Op1Kind = info.Op2Kind;
				info.Op1Index = 2;
				info.Op2Index = OpAccess_INVALID;
			}
		}
	}

	sealed class SimpleInstrInfo_AamAad : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_AamAad(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			if (instruction.Immediate8 == 10) {
				info = default;
				info.Mnemonic = mnemonic;
			}
			else
				info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
		}
	}

	static class StringUtils {
		public static InstrOpInfoFlags GetAddressSizeFlags(OpKind opKind) {
			switch (opKind) {
			case OpKind.MemorySegSI:
			case OpKind.MemorySegDI:
			case OpKind.MemoryESDI:
				return InstrOpInfoFlags.AddrSize16;

			case OpKind.MemorySegESI:
			case OpKind.MemorySegEDI:
			case OpKind.MemoryESEDI:
				return InstrOpInfoFlags.AddrSize32;

			case OpKind.MemorySegRSI:
			case OpKind.MemorySegRDI:
			case OpKind.MemoryESRDI:
				return InstrOpInfoFlags.AddrSize64;

			case OpKind.Register:
			case OpKind.NearBranch16:
			case OpKind.NearBranch32:
			case OpKind.NearBranch64:
			case OpKind.FarBranch16:
			case OpKind.FarBranch32:
			case OpKind.Immediate8:
			case OpKind.Immediate8_2nd:
			case OpKind.Immediate16:
			case OpKind.Immediate32:
			case OpKind.Immediate64:
			case OpKind.Memory64:
			case OpKind.Memory:
			default:
				return 0;
			}
		}
	}

	sealed class SimpleInstrInfo_YD : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_YD(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var opKind = instruction.Op0Kind;
			var shortFormOpKind = instruction.CodeSize switch {
				CodeSize.Unknown => opKind,
				CodeSize.Code16 => OpKind.MemoryESDI,
				CodeSize.Code32 => OpKind.MemoryESEDI,
				CodeSize.Code64 => OpKind.MemoryESRDI,
				_ => throw new InvalidOperationException(),
			};
			InstrOpInfoFlags flags = 0;
			if (opKind != shortFormOpKind)
				flags |= StringUtils.GetAddressSizeFlags(opKind);
			info = default;
			info.Flags = flags;
			info.Mnemonic = mnemonic;
		}
	}

	sealed class SimpleInstrInfo_DX : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_DX(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var opKind = instruction.Op1Kind;
			var shortFormOpKind = instruction.CodeSize switch {
				CodeSize.Unknown => opKind,
				CodeSize.Code16 => OpKind.MemorySegSI,
				CodeSize.Code32 => OpKind.MemorySegESI,
				CodeSize.Code64 => OpKind.MemorySegRSI,
				_ => throw new InvalidOperationException(),
			};
			InstrOpInfoFlags flags = 0;
			if (opKind != shortFormOpKind)
				flags |= StringUtils.GetAddressSizeFlags(opKind);
			info = default;
			info.Flags = flags;
			info.Mnemonic = mnemonic;
		}
	}

	sealed class SimpleInstrInfo_YX : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_YX(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var opKind = instruction.Op0Kind;
			var shortFormOpKind = instruction.CodeSize switch {
				CodeSize.Unknown => opKind,
				CodeSize.Code16 => OpKind.MemoryESDI,
				CodeSize.Code32 => OpKind.MemoryESEDI,
				CodeSize.Code64 => OpKind.MemoryESRDI,
				_ => throw new InvalidOperationException(),
			};
			InstrOpInfoFlags flags = 0;
			if (opKind != shortFormOpKind)
				flags |= StringUtils.GetAddressSizeFlags(opKind);
			info = default;
			info.Flags = flags;
			info.Mnemonic = mnemonic;
		}
	}

	sealed class SimpleInstrInfo_XY : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_XY(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var opKind = instruction.Op1Kind;
			var shortFormOpKind = instruction.CodeSize switch {
				CodeSize.Unknown => opKind,
				CodeSize.Code16 => OpKind.MemoryESDI,
				CodeSize.Code32 => OpKind.MemoryESEDI,
				CodeSize.Code64 => OpKind.MemoryESRDI,
				_ => throw new InvalidOperationException(),
			};
			InstrOpInfoFlags flags = 0;
			if (opKind != shortFormOpKind)
				flags |= StringUtils.GetAddressSizeFlags(opKind);
			info = default;
			info.Flags = flags;
			info.Mnemonic = mnemonic;
		}
	}

	sealed class SimpleInstrInfo_YA : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_YA(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var opKind = instruction.Op0Kind;
			var shortFormOpKind = instruction.CodeSize switch {
				CodeSize.Unknown => opKind,
				CodeSize.Code16 => OpKind.MemoryESDI,
				CodeSize.Code32 => OpKind.MemoryESEDI,
				CodeSize.Code64 => OpKind.MemoryESRDI,
				_ => throw new InvalidOperationException(),
			};
			InstrOpInfoFlags flags = 0;
			if (opKind != shortFormOpKind)
				flags |= StringUtils.GetAddressSizeFlags(opKind);
			info = default;
			info.Flags = flags;
			info.Mnemonic = mnemonic;
		}
	}

	sealed class SimpleInstrInfo_AX : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_AX(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var opKind = instruction.Op1Kind;
			var shortFormOpKind = instruction.CodeSize switch {
				CodeSize.Unknown => opKind,
				CodeSize.Code16 => OpKind.MemorySegSI,
				CodeSize.Code32 => OpKind.MemorySegESI,
				CodeSize.Code64 => OpKind.MemorySegRSI,
				_ => throw new InvalidOperationException(),
			};
			InstrOpInfoFlags flags = 0;
			if (opKind != shortFormOpKind)
				flags |= StringUtils.GetAddressSizeFlags(opKind);
			info = default;
			info.Flags = flags;
			info.Mnemonic = mnemonic;
		}
	}

	sealed class SimpleInstrInfo_AY : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_AY(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var opKind = instruction.Op1Kind;
			var shortFormOpKind = instruction.CodeSize switch {
				CodeSize.Unknown => opKind,
				CodeSize.Code16 => OpKind.MemoryESDI,
				CodeSize.Code32 => OpKind.MemoryESEDI,
				CodeSize.Code64 => OpKind.MemoryESRDI,
				_ => throw new InvalidOperationException(),
			};
			InstrOpInfoFlags flags = 0;
			if (opKind != shortFormOpKind)
				flags |= StringUtils.GetAddressSizeFlags(opKind);
			info = default;
			info.Flags = flags;
			info.Mnemonic = mnemonic;
		}
	}

	sealed class SimpleInstrInfo_XLAT : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_XLAT(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var baseReg = instruction.CodeSize switch {
				CodeSize.Unknown => instruction.MemoryBase,
				CodeSize.Code16 => Register.BX,
				CodeSize.Code32 => Register.EBX,
				CodeSize.Code64 => Register.RBX,
				_ => throw new InvalidOperationException(),
			};
			InstrOpInfoFlags flags = 0;
			var memBaseReg = instruction.MemoryBase;
			if (memBaseReg != baseReg) {
				if (memBaseReg == Register.BX)
					flags |= InstrOpInfoFlags.AddrSize16;
				else if (memBaseReg == Register.EBX)
					flags |= InstrOpInfoFlags.AddrSize32;
				else if (memBaseReg == Register.RBX)
					flags |= InstrOpInfoFlags.AddrSize64;
			}
			info = default;
			info.Flags = flags;
			info.Mnemonic = mnemonic;
		}
	}

	sealed class SimpleInstrInfo_nop : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;
		readonly Register register;

		public SimpleInstrInfo_nop(int bitness, string mnemonic, Register register) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
			this.register = register;
		}

		static readonly FormatterString str_xchg = new FormatterString("xchg");

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			int instrBitness = GetBitness(instruction.CodeSize);
			if (instrBitness == 0 || (instrBitness & bitness) != 0)
				info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
			else {
				info = default;
				info.Mnemonic = str_xchg;
				info.OpCount = 2;
				Static.Assert(InstrOpKind.Register == 0 ? 0 : -1);
				//info.Op0Kind = InstrOpKind.Register;
				//info.Op1Kind = InstrOpKind.Register;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)register;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op1Register = (byte)register;
				info.Op0Index = OpAccess_None;
				info.Op1Index = OpAccess_None;
			}
		}
	}

	sealed class SimpleInstrInfo_STIG1 : InstrInfo {
		readonly FormatterString mnemonic;
		readonly bool pseudoOp;

		public SimpleInstrInfo_STIG1(string mnemonic) : this(mnemonic, false) { }

		public SimpleInstrInfo_STIG1(string mnemonic, bool pseudoOp) {
			this.mnemonic = new FormatterString(mnemonic);
			this.pseudoOp = pseudoOp;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = default;
			info.Mnemonic = mnemonic;
			Debug.Assert(instruction.OpCount == 2);
			Debug.Assert(instruction.Op0Kind == OpKind.Register && instruction.Op0Register == Register.ST0);
			if (!pseudoOp || !(options.UsePseudoOps && instruction.Op1Register == Register.ST1)) {
				info.OpCount = 1;
				Static.Assert(InstrOpKind.Register == 0 ? 0 : -1);
				//info.Op0Kind = InstrOpKind.Register;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)instruction.Op1Register;
				info.Op0Index = 1;
			}
		}
	}

	sealed class SimpleInstrInfo_STIG2 : InstrInfo {
		readonly InstrOpInfoFlags flags;
		readonly FormatterString mnemonic;
		readonly bool pseudoOp;

		public SimpleInstrInfo_STIG2(string mnemonic, bool pseudoOp) : this(mnemonic, 0, pseudoOp) { }
		public SimpleInstrInfo_STIG2(string mnemonic, InstrOpInfoFlags flags) : this(mnemonic, flags, false) { }

		SimpleInstrInfo_STIG2(string mnemonic, InstrOpInfoFlags flags, bool pseudoOp) {
			this.flags = flags;
			this.mnemonic = new FormatterString(mnemonic);
			this.pseudoOp = pseudoOp;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = default;
			info.Flags = flags;
			info.Mnemonic = mnemonic;
			Debug.Assert(instruction.OpCount == 2);
			Debug.Assert(instruction.Op1Kind == OpKind.Register && instruction.Op1Register == Register.ST0);
			if (!pseudoOp || !(options.UsePseudoOps && instruction.Op0Register == Register.ST1)) {
				info.OpCount = 1;
				Static.Assert(InstrOpKind.Register == 0 ? 0 : -1);
				//info.Op0Kind = InstrOpKind.Register;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)instruction.Op0Register;
			}
		}
	}

	sealed class SimpleInstrInfo_as : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_as(int bitness, string mnemonic) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			var instrBitness = GetBitness(instruction.CodeSize);
			if (instrBitness != 0 && instrBitness != bitness) {
				if (bitness == 16)
					flags |= InstrOpInfoFlags.AddrSize16;
				else if (bitness == 32)
					flags |= InstrOpInfoFlags.AddrSize32;
				else
					flags |= InstrOpInfoFlags.AddrSize64;
			}
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_maskmovq : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_maskmovq(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			Debug.Assert(instruction.OpCount == 3);

			var instrBitness = GetBitness(instruction.CodeSize);

			var bitness = instruction.Op0Kind switch {
				OpKind.MemorySegDI => 16,
				OpKind.MemorySegEDI => 32,
				OpKind.MemorySegRDI => 64,
				_ => instrBitness,
			};

			info = default;
			info.Mnemonic = mnemonic;
			info.OpCount = 2;
			info.Op0Kind = (InstrOpKind)instruction.Op1Kind;
			info.Op0Index = 1;
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op0Register = (byte)instruction.Op1Register;
			info.Op1Kind = (InstrOpKind)instruction.Op2Kind;
			info.Op1Index = 2;
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op1Register = (byte)instruction.Op2Register;
			if (instrBitness != 0 && instrBitness != bitness) {
				if (bitness == 16)
					info.Flags |= InstrOpInfoFlags.AddrSize16;
				else if (bitness == 32)
					info.Flags |= InstrOpInfoFlags.AddrSize32;
				else
					info.Flags |= InstrOpInfoFlags.AddrSize64;
			}
		}
	}

	sealed class SimpleInstrInfo_pblendvb : InstrInfo {
		readonly FormatterString mnemonic;
		readonly MemorySize memSize;

		public SimpleInstrInfo_pblendvb(string mnemonic) : this(mnemonic, MemorySize.Unknown) { }

		public SimpleInstrInfo_pblendvb(string mnemonic, MemorySize memSize) {
			this.mnemonic = new FormatterString(mnemonic);
			this.memSize = memSize;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = default;
			Debug.Assert(instruction.OpCount == 2);
			info.Mnemonic = mnemonic;
			info.OpCount = 3;
			info.Op0Kind = (InstrOpKind)instruction.Op0Kind;
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op0Register = (byte)instruction.Op0Register;
			info.Op1Kind = (InstrOpKind)instruction.Op1Kind;
			info.Op1Index = 1;
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op1Register = (byte)instruction.Op1Register;
			Static.Assert(InstrOpKind.Register == 0 ? 0 : -1);
			//info.Op2Kind = InstrOpKind.Register;
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op2Register = (byte)Register.XMM0;
			info.MemorySize = memSize;
			info.Op2Index = OpAccess_Read;
		}
	}

	sealed class SimpleInstrInfo_reverse2 : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_reverse2(string mnemonic) => this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = default;
			info.Mnemonic = mnemonic;
			Debug.Assert(instruction.OpCount == 2);
			info.OpCount = 2;
			info.Op0Kind = (InstrOpKind)instruction.Op1Kind;
			info.Op0Index = 1;
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op0Register = (byte)instruction.Op1Register;
			info.Op1Kind = (InstrOpKind)instruction.Op0Kind;
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op1Register = (byte)instruction.Op0Register;
			info.MemorySize = instruction.MemorySize;
		}
	}

	sealed class SimpleInstrInfo_OpSize : InstrInfo {
		readonly CodeSize codeSize;
		readonly FormatterString[] mnemonics;

		public SimpleInstrInfo_OpSize(CodeSize codeSize, string mnemonic, string mnemonic16, string mnemonic32, string mnemonic64) {
			this.codeSize = codeSize;
			mnemonics = new FormatterString[4];
			mnemonics[(int)CodeSize.Unknown] = new FormatterString(mnemonic);
			mnemonics[(int)CodeSize.Code16] = new FormatterString(mnemonic16);
			mnemonics[(int)CodeSize.Code32] = new FormatterString(mnemonic32);
			mnemonics[(int)CodeSize.Code64] = new FormatterString(mnemonic64);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			FormatterString mnemonic;
			if (instruction.CodeSize == codeSize)
				mnemonic = mnemonics[(int)CodeSize.Unknown];
			else
				mnemonic = mnemonics[(int)codeSize];
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
		}
	}

	sealed class SimpleInstrInfo_OpSize2_bnd : InstrInfo {
		readonly FormatterString[] mnemonics;

		public SimpleInstrInfo_OpSize2_bnd(string mnemonic, string mnemonic16, string mnemonic32, string mnemonic64) {
			mnemonics = new FormatterString[4];
			mnemonics[(int)CodeSize.Unknown] = new FormatterString(mnemonic);
			mnemonics[(int)CodeSize.Code16] = new FormatterString(mnemonic16);
			mnemonics[(int)CodeSize.Code32] = new FormatterString(mnemonic32);
			mnemonics[(int)CodeSize.Code64] = new FormatterString(mnemonic64);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			if (instruction.HasRepnePrefix)
				flags |= InstrOpInfoFlags.BndPrefix;
			var mnemonic = mnemonics[(int)instruction.CodeSize];
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_OpSize3 : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonicDefault;
		readonly FormatterString mnemonicFull;

		public SimpleInstrInfo_OpSize3(int bitness, string mnemonicDefault, string mnemonicFull) {
			this.bitness = bitness;
			this.mnemonicDefault = new FormatterString(mnemonicDefault);
			this.mnemonicFull = new FormatterString(mnemonicFull);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var instrBitness = GetBitness(instruction.CodeSize);
			FormatterString mnemonic;
			if (instrBitness == 0 || (instrBitness & bitness) != 0)
				mnemonic = mnemonicDefault;
			else
				mnemonic = mnemonicFull;
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
		}
	}

	sealed class SimpleInstrInfo_os : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_os(int bitness, string mnemonic) : this(bitness, mnemonic, InstrOpInfoFlags.None) { }

		public SimpleInstrInfo_os(int bitness, string mnemonic, InstrOpInfoFlags flags) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = this.flags;
			int instrBitness = GetBitness(instruction.CodeSize);
			if (instrBitness != 0 && instrBitness != bitness) {
				if (bitness == 16)
					flags |= InstrOpInfoFlags.OpSize16;
				else if (bitness == 32)
					flags |= InstrOpInfoFlags.OpSize32;
				else
					flags |= InstrOpInfoFlags.OpSize64;
			}
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_os_mem : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_os_mem(int bitness, string mnemonic) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			int instrBitness = GetBitness(instruction.CodeSize);
			bool hasMemOp = instruction.Op0Kind == OpKind.Memory || instruction.Op1Kind == OpKind.Memory;
			if (hasMemOp && !(instrBitness == 0 || (instrBitness != 64 && instrBitness == bitness) || (instrBitness == 64 && bitness == 32))) {
				if (bitness == 16)
					flags |= InstrOpInfoFlags.OpSize16;
				else if (bitness == 32)
					flags |= InstrOpInfoFlags.OpSize32;
				else
					flags |= InstrOpInfoFlags.OpSize64;
			}
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_os_mem2 : InstrInfo {
		readonly InstrOpInfoFlags flags;
		readonly int bitness;
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_os_mem2(int bitness, string mnemonic, InstrOpInfoFlags flags) {
			this.flags = flags;
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = this.flags;
			int instrBitness = GetBitness(instruction.CodeSize);
			if (instrBitness != 0 && (instrBitness & bitness) == 0) {
				if (instrBitness != 16)
					flags |= InstrOpInfoFlags.OpSize16;
				else
					flags |= InstrOpInfoFlags.OpSize32;
			}
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_os_mem_reg16 : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_os_mem_reg16(int bitness, string mnemonic) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			int instrBitness = GetBitness(instruction.CodeSize);
			Debug.Assert(instruction.OpCount == 1);
			if (instruction.Op0Kind == OpKind.Memory) {
				if (!(instrBitness == 0 || (instrBitness != 64 && instrBitness == bitness) || (instrBitness == 64 && bitness == 32))) {
					if (bitness == 16)
						flags |= InstrOpInfoFlags.OpSize16;
					else if (bitness == 32)
						flags |= InstrOpInfoFlags.OpSize32;
					else
						flags |= InstrOpInfoFlags.OpSize64;
				}
			}
			info = new InstrOpInfo(mnemonic, instruction, flags);
			if (instruction.Op0Kind == OpKind.Register) {
				var reg = (Register)info.Op0Register;
				int regSize = 0;
				if (Register.AX <= reg && reg <= Register.R15W)
					regSize = 16;
				else if (Register.EAX <= reg && reg <= Register.R15D) {
					regSize = 32;
					reg = reg - Register.EAX + Register.AX;
				}
				else if (Register.RAX <= reg && reg <= Register.R15) {
					regSize = 64;
					reg = reg - Register.RAX + Register.AX;
				}
				Debug.Assert(regSize != 0);
				if (regSize != 0) {
					Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
					info.Op0Register = (byte)reg;
					if (!((instrBitness != 64 && instrBitness == regSize) || (instrBitness == 64 && regSize == 32))) {
						if (bitness == 16)
							info.Flags |= InstrOpInfoFlags.OpSize16;
						else if (bitness == 32)
							info.Flags |= InstrOpInfoFlags.OpSize32;
						else
							info.Flags |= InstrOpInfoFlags.OpSize64;
					}
				}
			}
		}
	}

	sealed class SimpleInstrInfo_os_jcc : InstrInfo {
		readonly int bitness;
		readonly int ccIndex;
		readonly FormatterString[] mnemonics;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_os_jcc(int bitness, int ccIndex, string[] mnemonics) : this(bitness, ccIndex, mnemonics, InstrOpInfoFlags.None) { }

		public SimpleInstrInfo_os_jcc(int bitness, int ccIndex, string[] mnemonics, InstrOpInfoFlags flags) {
			this.bitness = bitness;
			this.ccIndex = ccIndex;
			this.mnemonics = FormatterString.Create(mnemonics);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = this.flags;
			int instrBitness = GetBitness(instruction.CodeSize);
			if (flags != InstrOpInfoFlags.None) {
				if (instrBitness != 0 && instrBitness != bitness) {
					if (bitness == 16)
						flags |= InstrOpInfoFlags.OpSize16;
					else if (bitness == 32)
						flags |= InstrOpInfoFlags.OpSize32;
					else
						flags |= InstrOpInfoFlags.OpSize64;
				}
			}
			else {
				var branchInfo = BranchSizeInfo.Near;
				if (instrBitness != 0 && instrBitness != bitness) {
					if (bitness == 16)
						branchInfo = BranchSizeInfo.NearWord;
					else if (bitness == 32)
						branchInfo = BranchSizeInfo.NearDword;
				}
				flags |= (InstrOpInfoFlags)((int)branchInfo << (int)InstrOpInfoFlags.BranchSizeInfoShift);
			}
			var prefixSeg = instruction.SegmentPrefix;
			if (prefixSeg == Register.CS)
				flags |= InstrOpInfoFlags.JccNotTaken;
			else if (prefixSeg == Register.DS)
				flags |= InstrOpInfoFlags.JccTaken;
			if (instruction.HasRepnePrefix)
				flags |= InstrOpInfoFlags.BndPrefix;
			var mnemonic = MnemonicCC.GetMnemonicCC(options, ccIndex, mnemonics);
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_os_loop : InstrInfo {
		readonly int bitness;
		readonly int ccIndex;
		readonly Register register;
		readonly FormatterString[] mnemonics;

		public SimpleInstrInfo_os_loop(int bitness, int ccIndex, Register register, string[] mnemonics) {
			this.bitness = bitness;
			this.ccIndex = ccIndex;
			this.register = register;
			this.mnemonics = FormatterString.Create(mnemonics);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			int instrBitness = GetBitness(instruction.CodeSize);
			var expectedReg = instrBitness switch {
				0 => register,
				16 => Register.CX,
				32 => Register.ECX,
				64 => Register.RCX,
				_ => throw new InvalidOperationException(),
			};
			bool addReg = expectedReg != register;
			if (instrBitness != 0 && instrBitness != bitness) {
				if (bitness == 16)
					flags |= InstrOpInfoFlags.OpSize16;
				else if (bitness == 32)
					flags |= InstrOpInfoFlags.OpSize32;
				else
					flags |= InstrOpInfoFlags.OpSize64;
			}
			var mnemonic = ccIndex == -1 ? mnemonics[0] : MnemonicCC.GetMnemonicCC(options, ccIndex, mnemonics);
			info = new InstrOpInfo(mnemonic, instruction, flags);
			if (addReg) {
				Debug.Assert(info.OpCount == 1);
				info.OpCount = 2;
				info.Op1Kind = InstrOpKind.Register;
				info.Op1Index = OpAccess_ReadWrite;
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op1Register = (byte)register;
			}
		}
	}

	sealed class SimpleInstrInfo_os_call : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;
		readonly bool canHaveBndPrefix;

		public SimpleInstrInfo_os_call(int bitness, string mnemonic, bool canHaveBndPrefix = false) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
			this.canHaveBndPrefix = canHaveBndPrefix;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			if (canHaveBndPrefix && instruction.HasRepnePrefix)
				flags |= InstrOpInfoFlags.BndPrefix;
			int instrBitness = GetBitness(instruction.CodeSize);
			var branchInfo = BranchSizeInfo.None;
			if (instrBitness != 0 && instrBitness != bitness) {
				if (bitness == 16)
					branchInfo = BranchSizeInfo.Word;
				else if (bitness == 32)
					branchInfo = BranchSizeInfo.Dword;
			}
			flags |= (InstrOpInfoFlags)((int)branchInfo << (int)InstrOpInfoFlags.BranchSizeInfoShift);
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_far : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_far(int bitness, string mnemonic) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			int instrBitness = GetBitness(instruction.CodeSize);
			var branchInfo = BranchSizeInfo.None;
			if (instrBitness != 0 && instrBitness != bitness) {
				if (bitness == 16)
					branchInfo = BranchSizeInfo.Word;
				else
					branchInfo = BranchSizeInfo.Dword;
			}
			flags |= (InstrOpInfoFlags)((int)branchInfo << (int)InstrOpInfoFlags.BranchSizeInfoShift);
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_far_mem : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_far_mem(int bitness, string mnemonic) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.ShowNoMemSize_ForceSize;
			int instrBitness = GetBitness(instruction.CodeSize);
			var farMemSizeInfo = FarMemorySizeInfo.None;
			if (instrBitness != 0 && instrBitness != bitness) {
				if (bitness == 16)
					farMemSizeInfo = FarMemorySizeInfo.Word;
				else
					farMemSizeInfo = FarMemorySizeInfo.Dword;
			}
			flags |= (InstrOpInfoFlags)((int)farMemSizeInfo << (int)InstrOpInfoFlags.FarMemorySizeInfoShift);
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_movabs : InstrInfo {
		readonly int memOpNumber;
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_movabs(int memOpNumber, string mnemonic) {
			this.memOpNumber = memOpNumber;
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = InstrOpInfoFlags.None;
			int instrBitness = GetBitness(instruction.CodeSize);
			var opKind = instruction.GetOpKind(memOpNumber);
			int memSize;
			if (opKind == OpKind.Memory64)
				memSize = 64;
			else {
				Debug.Assert(opKind == OpKind.Memory);
				memSize = instruction.MemoryDisplSize == 2 ? 16 : 32;
			}
			if (instrBitness == 0)
				instrBitness = memSize;
			var memSizeInfo = MemorySizeInfo.None;
			if (instrBitness == 64) {
				if (memSize == 32)
					flags |= InstrOpInfoFlags.AddrSize32;
				else
					memSizeInfo = MemorySizeInfo.Qword;
			}
			else if (instrBitness != memSize) {
				Debug.Assert(memSize == 16 || memSize == 32);
				if (memSize == 16)
					memSizeInfo = MemorySizeInfo.Word;
				else
					memSizeInfo = MemorySizeInfo.Dword;
			}
			flags |= (InstrOpInfoFlags)((int)memSizeInfo << (int)InstrOpInfoFlags.MemorySizeInfoShift);
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_er : InstrInfo {
		readonly int erIndex;
		readonly FormatterString mnemonic;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_er(int erIndex, string mnemonic) : this(erIndex, mnemonic, InstrOpInfoFlags.None) { }

		public SimpleInstrInfo_er(int erIndex, string mnemonic, InstrOpInfoFlags flags) {
			this.erIndex = erIndex;
			this.mnemonic = new FormatterString(mnemonic);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, flags);
			var rc = instruction.RoundingControl;
			if (rc != RoundingControl.None) {
				if (!FormatterUtils.CanShowRoundingControl(instruction, options))
					return;
				InstrOpKind rcOpKind;
				switch (rc) {
				case RoundingControl.RoundToNearest:	rcOpKind = InstrOpKind.RnSae; break;
				case RoundingControl.RoundDown:			rcOpKind = InstrOpKind.RdSae; break;
				case RoundingControl.RoundUp:			rcOpKind = InstrOpKind.RuSae; break;
				case RoundingControl.RoundTowardZero:	rcOpKind = InstrOpKind.RzSae; break;
				default:
					return;
				}
				MoveOperands(ref info, erIndex, rcOpKind);
			}
		}

		internal static void MoveOperands(ref InstrOpInfo info, int index, InstrOpKind newOpKind) {
			Debug.Assert(info.OpCount <= 4);

			switch (index) {
			case 2:
				Debug.Assert(info.OpCount < 4 || info.Op3Kind != InstrOpKind.Register);
				info.Op4Kind = info.Op3Kind;
				info.Op3Kind = info.Op2Kind;
				info.Op3Register = info.Op2Register;
				info.Op2Kind = newOpKind;
				info.Op4Index = info.Op3Index;
				info.Op3Index = info.Op2Index;
				info.Op2Index = OpAccess_None;
				info.OpCount++;
				break;

			case 3:
				Debug.Assert(info.OpCount < 4 || info.Op3Kind != InstrOpKind.Register);
				info.Op4Kind = info.Op3Kind;
				info.Op3Kind = newOpKind;
				info.Op4Index = info.Op3Index;
				info.Op3Index = OpAccess_None;
				info.OpCount++;
				break;

			default:
				throw new InvalidOperationException();
			}
		}
	}

	sealed class SimpleInstrInfo_sae : InstrInfo {
		readonly int saeIndex;
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_sae(int saeIndex, string mnemonic) {
			this.saeIndex = saeIndex;
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
			if (instruction.SuppressAllExceptions)
				SimpleInstrInfo_er.MoveOperands(ref info, saeIndex, InstrOpKind.Sae);
		}
	}

	sealed class SimpleInstrInfo_bcst : InstrInfo {
		readonly FormatterString mnemonic;
		readonly InstrOpInfoFlags flagsNoBroadcast;
		readonly InstrOpInfoFlags flagsBroadcast;

		public SimpleInstrInfo_bcst(string mnemonic, InstrOpInfoFlags flagsNoBroadcast, InstrOpInfoFlags flagsBroadcast) {
			this.mnemonic = new FormatterString(mnemonic);
			this.flagsNoBroadcast = flagsNoBroadcast;
			this.flagsBroadcast = flagsBroadcast;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var memInfo = MemorySizes.AllMemorySizes[(int)instruction.MemorySize];
			var flags = !memInfo.bcstTo.IsDefault ? flagsBroadcast : flagsNoBroadcast;
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_bnd : InstrInfo {
		readonly FormatterString mnemonic;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_bnd(string mnemonic) : this(mnemonic, InstrOpInfoFlags.None) { }

		public SimpleInstrInfo_bnd(string mnemonic, InstrOpInfoFlags flags) {
			this.mnemonic = new FormatterString(mnemonic);
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			var flags = this.flags;
			if (instruction.HasRepnePrefix)
				flags |= InstrOpInfoFlags.BndPrefix;
			info = new InstrOpInfo(mnemonic, instruction, flags);
		}
	}

	sealed class SimpleInstrInfo_pops : InstrInfo {
		readonly FormatterString mnemonic;
		readonly FormatterString[] pseudo_ops;
		readonly InstrOpInfoFlags flags;

		public SimpleInstrInfo_pops(string mnemonic, FormatterString[] pseudo_ops) : this(mnemonic, pseudo_ops, InstrOpInfoFlags.None) { }

		public SimpleInstrInfo_pops(string mnemonic, FormatterString[] pseudo_ops, InstrOpInfoFlags flags) {
			this.mnemonic = new FormatterString(mnemonic);
			this.pseudo_ops = pseudo_ops;
			this.flags = flags;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, flags);
			int imm = instruction.Immediate8;
			if (options.UsePseudoOps && (uint)imm < (uint)pseudo_ops.Length) {
				info.Mnemonic = pseudo_ops[imm];
				RemoveLastOp(ref info);
			}
		}

		internal static void RemoveLastOp(ref InstrOpInfo info) {
			switch (info.OpCount) {
			case 5:
				info.Op4Index = OpAccess_INVALID;
				break;
			case 4:
				info.Op3Index = OpAccess_INVALID;
				break;
			case 3:
				info.Op2Index = OpAccess_INVALID;
				break;
			default:
				throw new InvalidOperationException();
			}
			info.OpCount--;
		}
	}

	sealed class SimpleInstrInfo_sae_pops : InstrInfo {
		readonly int saeIndex;
		readonly FormatterString mnemonic;
		readonly FormatterString[] pseudo_ops;

		public SimpleInstrInfo_sae_pops(int saeIndex, string mnemonic, FormatterString[] pseudo_ops) {
			this.saeIndex = saeIndex;
			this.mnemonic = new FormatterString(mnemonic);
			this.pseudo_ops = pseudo_ops;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
			if (instruction.SuppressAllExceptions)
				SimpleInstrInfo_er.MoveOperands(ref info, saeIndex, InstrOpKind.Sae);
			int imm = instruction.Immediate8;
			if (options.UsePseudoOps && (uint)imm < (uint)pseudo_ops.Length) {
				info.Mnemonic = pseudo_ops[imm];
				SimpleInstrInfo_pops.RemoveLastOp(ref info);
			}
		}
	}

	sealed class SimpleInstrInfo_ms_pops : InstrInfo {
		readonly FormatterString mnemonic;
		readonly FormatterString[] pseudo_ops;
		readonly InstrOpInfoFlags flags;
		readonly MemorySize memSize;

		public SimpleInstrInfo_ms_pops(string mnemonic, FormatterString[] pseudo_ops, InstrOpInfoFlags flags, MemorySize memSize) {
			this.mnemonic = new FormatterString(mnemonic);
			this.pseudo_ops = pseudo_ops;
			this.flags = flags;
			this.memSize = memSize;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, flags);
			if (memSize != MemorySize.Unknown)
				info.MemorySize = memSize;
			int imm = instruction.Immediate8;
			if (options.UsePseudoOps && (uint)imm < (uint)pseudo_ops.Length) {
				info.Mnemonic = pseudo_ops[imm];
				SimpleInstrInfo_pops.RemoveLastOp(ref info);
			}
		}
	}

	sealed class SimpleInstrInfo_pclmulqdq : InstrInfo {
		readonly FormatterString mnemonic;
		readonly FormatterString[] pseudo_ops;

		public SimpleInstrInfo_pclmulqdq(string mnemonic, FormatterString[] pseudo_ops) {
			this.mnemonic = new FormatterString(mnemonic);
			this.pseudo_ops = pseudo_ops;
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.None);
			if (options.UsePseudoOps) {
				int index;
				int imm = instruction.Immediate8;
				if (imm == 0)
					index = 0;
				else if (imm == 1)
					index = 1;
				else if (imm == 0x10)
					index = 2;
				else if (imm == 0x11)
					index = 3;
				else
					index = -1;
				if (index >= 0) {
					info.Mnemonic = pseudo_ops[index];
					SimpleInstrInfo_pops.RemoveLastOp(ref info);
				}
			}
		}
	}

	sealed class SimpleInstrInfo_Reg16 : InstrInfo {
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_Reg16(string mnemonic) =>
			this.mnemonic = new FormatterString(mnemonic);

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			const InstrOpInfoFlags flags = InstrOpInfoFlags.None;
			info = new InstrOpInfo(mnemonic, instruction, flags);
			if (Register.EAX <= (Register)info.Op0Register && (Register)info.Op0Register <= Register.R15D) {
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)((Register)info.Op0Register - Register.EAX + Register.AX);
			}
			if (Register.EAX <= (Register)info.Op1Register && (Register)info.Op1Register <= Register.R15D) {
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op1Register = (byte)((Register)info.Op1Register - Register.EAX + Register.AX);
			}
		}
	}

	sealed class SimpleInstrInfo_invlpga : InstrInfo {
		readonly int bitness;
		readonly FormatterString mnemonic;

		public SimpleInstrInfo_invlpga(int bitness, string mnemonic) {
			this.bitness = bitness;
			this.mnemonic = new FormatterString(mnemonic);
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = default;
			info.Mnemonic = mnemonic;
			info.OpCount = 2;
			info.Op0Kind = InstrOpKind.Register;
			info.Op1Kind = InstrOpKind.Register;
			Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
			info.Op1Register = (byte)Register.ECX;
			info.Op0Index = OpAccess_Read;
			info.Op1Index = OpAccess_Read;

			switch (bitness) {
			case 16:
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)Register.AX;
				break;

			case 32:
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)Register.EAX;
				break;

			case 64:
				Static.Assert(InstrOpInfo.TEST_RegisterBits == 8 ? 0 : -1);
				info.Op0Register = (byte)Register.RAX;
				break;

			default:
				throw new InvalidOperationException();
			}
		}
	}

	sealed class SimpleInstrInfo_DeclareData : InstrInfo {
		readonly FormatterString mnemonic;
		readonly InstrOpKind opKind;

		public SimpleInstrInfo_DeclareData(Code code, string mnemonic) {
			this.mnemonic = new FormatterString(mnemonic);
			opKind = code switch {
				Code.DeclareByte => InstrOpKind.DeclareByte,
				Code.DeclareWord => InstrOpKind.DeclareWord,
				Code.DeclareDword => InstrOpKind.DeclareDword,
				Code.DeclareQword => InstrOpKind.DeclareQword,
				_ => throw new InvalidOperationException(),
			};
		}

		public override void GetOpInfo(FormatterOptions options, in Instruction instruction, out InstrOpInfo info) {
			info = new InstrOpInfo(mnemonic, instruction, InstrOpInfoFlags.MnemonicIsDirective);
			info.OpCount = (byte)instruction.DeclareDataCount;
			info.Op0Kind = opKind;
			info.Op1Kind = opKind;
			info.Op2Kind = opKind;
			info.Op3Kind = opKind;
			info.Op4Kind = opKind;
			info.Op0Index = OpAccess_Read;
			info.Op1Index = OpAccess_Read;
			info.Op2Index = OpAccess_Read;
			info.Op3Index = OpAccess_Read;
			info.Op4Index = OpAccess_Read;
		}
	}
}
#endif
